﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Windows.Forms;
using System.ComponentModel.Design;

namespace ProgNetComponents.MDI
{
    public class MdiHelper : Component
    {
        #region Members
        public const string SeparateString = "#\f#";
        private Form _MdiParent;
        private Dictionary<string, List<MdiChildClass>> AllChildren = new Dictionary<string, List<MdiChildClass>>();
        #endregion

        #region Properties
        [RefreshProperties(RefreshProperties.All)]
        public Form MdiParent
        {
            get { return _MdiParent; }
            set
            {
                if (_MdiParent != null)
                {
                    _MdiParent.ResizeBegin -= new EventHandler(_MdiParent_ResizeBegin);
                    _MdiParent.ResizeEnd -= new EventHandler(_MdiParent_ResizeEnd);
                }
                _MdiParent = value;
                if (_MdiParent != null)
                {
                    _MdiParent.ResizeBegin += new EventHandler(_MdiParent_ResizeBegin);
                    _MdiParent.ResizeEnd += new EventHandler(_MdiParent_ResizeEnd);
                }
            }
        }

        public bool MaximizeForms
        {
            get;
            set;
        }

        void _MdiParent_ResizeEnd(object sender, EventArgs e)
        {
            Form[] forms = (sender as Form).MdiChildren;
            if (forms != null)
            {
                foreach (Form f in forms)
                {
                    f.ResumeLayout(true);
                }
            }
        }

        void _MdiParent_ResizeBegin(object sender, EventArgs e)
        {
            Form[] forms = (sender as Form).MdiChildren;
            if (forms != null)
            {
                foreach (Form f in forms)
                {
                    f.SuspendLayout();
                }
            }
        }

        #endregion

        #region Events
        public event EventHandler<CompareArgsEventArgs> CompareParameters;
        public event EventHandler<CreateParameterEventArgs> CreateParameter;
        public event EventHandler<ParameterToStringEventArgs> ParameterToString;
        public event EventHandler FormCreated;

        #endregion

        #region CTOR
        public MdiHelper()
        {

        }
        public MdiHelper(IContainer container) 
        {
            if (container != null)
            {
                container.Add(this);
                foreach (object o in container.Components)
                {
                    if (o is MdiHelper && o != this)
                        throw new Exception("Only one MdiHelper can be added.");
                }
                IDesignerEventService service = this.GetService(typeof(IDesignerEventService)) as IDesignerEventService;
                if (service != null)
                {
                    if (service.ActiveDesigner != null && service.ActiveDesigner.RootComponent != null &&
                        service.ActiveDesigner.RootComponent is Form)
                    {
                        MdiParent = service.ActiveDesigner.RootComponent as Form;
                    }
                }
            }
        }
        #endregion

        #region Public Methods
        public string GetOpenedFormsString()
        {
            string str = "";
            List<string> list = GetOpenedFormsStringList();
            foreach (string s in list)
            {
                str = str.AppendString(SeparateString);
                str += s;
            }
            return str;
        }

        public void OpenFormsFromString(string str)
        {
            List<string> list = (str + SeparateString).GetMatches("(.*?)" + SeparateString);
            OpenFormsFromStringList(list);
        }

        public void OpenFormsFromStringList(List<string> list)
        {
            Type type = null;
            bool show = false;
            List<Dictionary<int, string>> lista = null;
            List<object> paramsList = null;
            string[] parts = null;
            Type paramType;
            bool isOk = true;
            foreach (string s in list)
            {
                paramsList = null;
                show = false;
                isOk = true;
                lista = s.GetMatches("([^;]*);([^;]*);(.*)", new int[] { 1, 2, 3 });
                if (lista.Count == 1)
                {
                    if (lista[0].Count == 3)
                    {
                        try
                        {
                            type = lista[0][1].GetAssemblyType();

                            if (type == null)
                                continue;
                            bool.TryParse(lista[0][2], out show);
                            if (lista[0][3].IsNotNull())
                            {
                                parts = lista[0][3].Split('\f');
                                foreach (string part in parts)
                                {
                                    if (paramsList == null)
                                        paramsList = new List<object>();
                                    paramType = part.GetFirstMatch("([^;]*);.*").GetAssemblyType();
                                    paramsList.Add(OnCreateParameter(paramType, part.GetFirstMatch(".*;(.*)")));
                                }
                            }
                        }
                        catch
                        {
                            isOk = false;
                            continue;
                        }
                        if (isOk)
                        {
                            object[] paramsTab = null;
                            if (paramsList != null)
                                paramsTab = paramsList.ToArray();
                            GetMdiChildFull(type, true, show, true, paramsTab);
                        }
                    }
                }
            }
        }

        public string GetActiveChildString()
        {
            string type = "";
            string shown = "";
            string paramStr = "";
            foreach (KeyValuePair<string, List<MdiChildClass>> kvp in AllChildren)
            {
                type = kvp.Key;
                foreach (MdiChildClass mdi in kvp.Value)
                {
                    if (mdi.Form == this.MdiParent.ActiveMdiChild)
                    {
                        shown = mdi.Form.Visible.ToString();
                        paramStr = "";
                        if (mdi.Parameters != null)
                        {
                            foreach (object o in mdi.Parameters)
                            {
                                if (o != null)
                                {
                                    paramStr = paramStr.AppendString("\f");
                                    paramStr += o.GetType().FullName + ";" + o.ToString();
                                }
                            }
                        }
                        return type + ";" + shown + ";" + paramStr;
                    }
                }
            }
            return "";
        }

        public List<string> GetOpenedFormsStringList()
        {
            List<string> list = new List<string>();
            string type = "";
            string shown = "";
            string paramStr = "";
            foreach (KeyValuePair<string, List<MdiChildClass>> kvp in AllChildren)
            {
                type = kvp.Key;
                foreach (MdiChildClass mdi in kvp.Value)
                {
                    shown = mdi.Form.Visible.ToString();
                    paramStr = "";
                    if (mdi.Parameters != null)
                    {
                        foreach (object o in mdi.Parameters)
                        {
                            paramStr = paramStr.AppendString("\f");
                            paramStr += ParamToString(o);
                        }
                    }
                    list.Add(type + ";" + shown + ";" + paramStr);
                }
            }
            return list;
        }

        public Form GetMdiChildFull(Type childType, bool create, bool showIfCreate, bool showIfHidden, params object[] args)
        {
            return GetMdiChildFull(childType, true, create, showIfCreate, showIfHidden, args);
        }
        public Form GetMdiChildFull(Type childType, bool remember, bool create, bool showIfCreate, bool showIfHidden, params object[] args)
        {
            Form f = null;
            if (MdiParent != null && MdiParent.IsMdiContainer && childType != null)
            {
                List<MdiChildClass> children = null;
                if (AllChildren.ContainsKey(childType.FullName))
                    children = AllChildren[childType.FullName];
                else
                {
                    children = new List<MdiChildClass>();
                    AllChildren.Add(childType.FullName, children);
                }
                bool wasCreated = false;
                CompareArgsEventArgs e = null;
                foreach (MdiChildClass mdi in children)
                {
                    e = new CompareArgsEventArgs(args, mdi.Parameters, childType);
                    e.TheSame = CompareArgs(args, mdi.Parameters);
                    if (CompareParameters != null)
                    {
                        CompareParameters(this, e);
                    }
                    if (e.TheSame)
                    {
                        f = mdi.Form;
                        break;
                    }
                }
                if (f == null && create)
                {
                    wasCreated = true;
                    f = CreateForm(childType, args);
                    if (f != null && remember)
                    {
                        f.MdiParent = MdiParent;
                        MdiChildClass mdi = new MdiChildClass();
                        if (args != null && args.Length == 0)
                            args = null;
                        mdi.Parameters = args;
                        mdi.Form = f;
                        children.Add(mdi);
                    }
                }
                if ((!wasCreated && showIfHidden) || (wasCreated && showIfCreate))
                {
                    if (f != null)
                    {
                        if (MaximizeForms)
                            f.WindowState = FormWindowState.Maximized;
                        f.Show();
                        f.BringToFront();
                    }
                }
            }

            return f;
        }

        public Form GetMdiChild(Type childType, bool remember, params object[] args)
        {
            return GetMdiChildFull(childType, remember, true, true, true, args);
        }
        public Form GetMdiChildAndRemember(Type childType, params object[] args)
        {
            return GetMdiChild(childType, true, args);
        }
        public Form GetMdiChild(Type childType)
        {
            return GetMdiChild(childType, true);
        }

        public bool TryAddChild(Form f, params object[] args)
        {
            bool added = false;
            if (f != null)
            {
                Type childType = f.GetType();
                if (MdiParent != null && MdiParent.IsMdiContainer && childType != null)
                {
                    List<MdiChildClass> children = null;
                    if (AllChildren.ContainsKey(childType.FullName))
                        children = AllChildren[childType.FullName];
                    else
                    {
                        children = new List<MdiChildClass>();
                        AllChildren.Add(childType.FullName, children);
                    }

                    CompareArgsEventArgs e = null;
                    foreach (MdiChildClass mdi in children)
                    {
                        e = new CompareArgsEventArgs(args, mdi.Parameters, childType);
                        e.TheSame = CompareArgs(args, mdi.Parameters);
                        if (CompareParameters != null)
                        {
                            CompareParameters(this, e);
                        }
                        if (e.TheSame)
                        {
                            return false;
                        }
                    }
                    MdiChildClass m = new MdiChildClass();
                    m.Form = f;
                    m.Parameters = args;
                    children.Add(m);
                    added = true;
                    f.FormClosed -= new FormClosedEventHandler(f_FormClosed);
                    f.FormClosed += new FormClosedEventHandler(f_FormClosed);
                }
            }
            return added;
        }
        #endregion

        #region private methods

        private object OnCreateParameter(Type paramType, string value)
        {
            object o = null;
            try
            {
                o = Convert.ChangeType(value, paramType);
            }
            catch { }
            if (CreateParameter != null)
            {
                CreateParameterEventArgs e = new CreateParameterEventArgs(value, paramType);
                CreateParameter(this, e);
                o = e.Parameter;
            }
            return o;
        }
        private string ParamToString(object parameter)
        {
            if (parameter != null)
            {
                string str = parameter.GetType().FullName + ";";
                string val = "";
                if (ParameterToString != null)
                {
                    ParameterToStringEventArgs e = new ParameterToStringEventArgs(parameter);
                    ParameterToString(this, e);
                    val = e.StringValue;
                    str += val;
                }
                if (val.IsNull())
                {
                    str += parameter.ToString();
                }
                return str;
            }
            return string.Empty;
        }
        private bool CompareArgs(object[] args1, object[] args2)
        {
            if (args1 == null && args2 == null)
                return true;
            if (args1 == null && args2.Length == 0)
                return true;
            if (args2 == null && args1.Length == 0)
                return true;
            if (args1 == null || args2 == null)
                return false;
            if (args1.Length != args2.Length)
                return false;
            for (int i = 0; i < args1.Length; i++)
            {
                if (args1[i] == null && args2[i] == null)
                    continue;
                if (args1[i] == null)
                    return false;
                if (!args1[i].Equals(args2[i]))
                    return false;
            }
            return true;
        }

        private Form CreateForm(Type childType, params object[] args)
        {
            Form f = null;
            try
            {
                f = Activator.CreateInstance(childType, args) as Form;
                if (f != null)
                {
                    f.FormClosed += new FormClosedEventHandler(f_FormClosed);
                }
            }
            catch (Exception ex) { ex.Log(); ex.InnerException.Log(); }
            if (FormCreated != null && f!=null)
                FormCreated(f, EventArgs.Empty);
            return f;
        }

        private void f_FormClosed(object sender, FormClosedEventArgs e)
        {
            Form f = sender as Form;
            if (AllChildren.ContainsKey(f.GetType().FullName))
            {
                List<MdiChildClass> list = AllChildren[f.GetType().FullName];
                MdiChildClass m = null;
                if (list != null)
                {
                    foreach (MdiChildClass mdi in list)
                    {
                        if (mdi.Form == f)
                        {
                            m = mdi;
                            break;
                        }
                    }
                    if (m != null)
                        list.Remove(m);
                }
            }
        }

        #endregion

        private class MdiChildClass
        {
            public object[] Parameters
            {
                get;
                set;
            }
            public Form Form
            {
                get;
                set;
            }
        }
    }

    public class CompareArgsEventArgs : EventArgs
    {
        public CompareArgsEventArgs(object[] params1, object[] params2, Type childType)
        {
            this._ChildType = childType;
            this._Params1 = params1;
            this._Params2 = params2;
        }

        private object[] _Params1;
        private object[] _Params2;
        private Type _ChildType;

        public object[] Params1
        {
            get
            {
                return _Params1;
            }
        }
        public object[] Params2
        {
            get
            {
                return _Params2;
            }
        }
        public Type ChildType
        {
            get { return _ChildType; }
        }
        public bool TheSame
        {
            get;
            set;
        }

    }
    public class CreateParameterEventArgs : EventArgs
    {
        public CreateParameterEventArgs(string stringValue, Type paramType)
        {
            this._StringValue = stringValue;
            this._ParamType = paramType;
        }

        private string _StringValue;
        public string StringValue
        {
            get
            {
                return _StringValue;
            }
        }

        private Type _ParamType;
        public Type ParamType
        {
            get { return _ParamType; }
        }
        public object Parameter
        {
            get;
            set;
        }

    }
    public class ParameterToStringEventArgs : EventArgs
    {
        public ParameterToStringEventArgs(object Parameter)
        {
            this._Parameter = Parameter;
        }

        private object _Parameter;
        public string StringValue
        {
            get;
            set;
        }

        public object Parameter
        {
            get { return _Parameter; }
        }

    }
}
